/** @file    TelnetServerBundle.cpp
 *  @author  Alessandro Polo
 *  @version $Id: TelnetServerBundle.cpp 2883 2010-08-09 14:51:56Z alex $
 *  @brief
 * File containing methods for the wosh::services::TelnetServerBundle class.
 * The header for this class can be found in TelnetServerBundle.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "TelnetServerBundle.h"
  #include "TelnetServerImpl.h"

 #include <core/ObjectAllocator.h>
 #include <core/WoshKernel.h>
 #include <core/SessionManager.h>
 #include <core/MessageFilter.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/Notification.h>
 #include <core/Fact.h>
 #include <core/Table.h>
 #include <core/DataModels.h>
 #include <interfaces/services/CommunicatorService.h>
 #include <framework/communication/SessionIM.h>
 #include <framework/communication/TextMessage.h>


using namespace wosh;
using namespace wosh::communication;
namespace wosh {
 namespace services {

 WOSH_REGISTER(wosh::services::TelnetServerBundle, "wosh::interfaces::services::TelnetServerBundle", _TelnetServer_VERSION, _static_TelnetServerBundle1 )
 WOSH_REGISTER(wosh::services::TelnetServerBundle, "wosh::BundleGeneric", _TelnetServer_VERSION, _static_TelnetServerBundle2 )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

TelnetServerBundle::TelnetServerBundle()
	: BundleGeneric() {
	BundleGeneric::setName( "TelnetServer" );
	Properties.update( _KEY_Version, _TelnetServer_VERSION );

Log.setLevel( LOG_VERBOSE );

	Log(LOG_DEBUG, " Configuring TelnetServer worker.." );
	this->ssrvWorker = new TelnetServerImpl(*this);
	this->ssrvWorker->setListener(this);
	this->ssrvWorker->setMaxConnections(10);

	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _TelnetServer_KEY_WelcomeString, Utilities::format("Welcome WOSH TelnetServer v.%g",_TelnetServer_VERSION), Permission::RW_RW_R__() );
	Properties.set( _TelnetServer_KEY_BindAddressPort, "", Permission::RW_RW_R__() );
	Properties.set( _TelnetServer_KEY_MaxConnections, 10, Permission::RW_RW_R__() );
	Properties.set( _TelnetServer_KEY_MaxRetries,	3, Permission::RW_RW_R__() );
	Properties.set( _TelnetServer_KEY_PingTimeout,	360, Permission::RW_RW_R__() );
	Properties.set( _TelnetServer_KEY_GreenList,	"", Permission::RW_R__R__() );
	Properties.set( _TelnetServer_KEY_YellowList,	"", Permission::RW_R__R__() );
	Properties.set( _TelnetServer_KEY_BlackList,	"", Permission::RW_R__R__() );
	Properties.set( _TelnetServer_KEY_GrantRootAccess, false, Permission::RW_R__R__() );
	Properties.set( _TelnetServer_KEY_GrantSudo, true, Permission::RW_R__R__() );

	Log(LOG_DEBUG, " Registering methods.." );

	MethodList* mmListSessions = Methods.create<MethodList>( _METHOD_List, "List Sessions", Permission::R_XR_XR__() );
	mmListSessions->setDataFieldExtractor( DataFieldExtractor<TelnetSession>::createFor< DataAdapterMapT<long,TelnetSession> >(this->ssrvWorker->getSessions(), "Sessions") );
	
	MethodMessageResponse* mmDisconnect = Methods.create<MethodMessageResponse>( "disconnect", "disconnect session(s)", Permission(Permission::RX) );
	mmDisconnect->setMethod( this, (MethodMessageResponsePtr)&TelnetServerBundle::mmDoDisconnect );


	setBundleState(Bundle::STATE_CREATED, false);
 }

TelnetServerBundle::~TelnetServerBundle() {
	Log(LOG_VERBOSE, " Destroying.." );
	if ( isBundleRunning() ) {
		Log(LOG_WARNING, "~TelnetServerBundle() : Destroying while RUNNING! Trying to stop.." );
		bundleStop();
	 }
	delete this->ssrvWorker; this->ssrvWorker = NULL;
	Log(LOG_INFO, ":~TelnetServerBundle() : Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL

WRESULT TelnetServerBundle::bundleStart() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STARTING) ) return WRET_ERR_WRONG_STATE;
	std::string addressport = Properties.getValueOf(_TelnetServer_KEY_BindAddressPort).toString();
	WRESULT ret = this->ssrvWorker->listen( addressport, 30000 );
	// BUNDLE-STATE (STARTED) will be updated async by WORKER, through call: thread_event()
	return ret;
 }

WRESULT TelnetServerBundle::bundleStop() {
	if ( !BundleGeneric::bundleValidate_StartStop(Bundle::STATE_STOPPING) ) return WRET_ERR_WRONG_STATE;
	WRESULT ret = this->ssrvWorker->stopListening( 30000 );
	// BUNDLE-STATE (STOPPED) will be updated async by WORKER, through call: thread_event()
	return ret;
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUNDLE CONTROL
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES

void TelnetServerBundle::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	// notifications and messages for sessions are handled/forwarded by SessionManager!
	BundleGeneric::busMessage(message, source);
 }

//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT

bool TelnetServerBundle::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source ) {
	if ( property_current.getKey() == _TelnetServer_KEY_BindAddressPort ) {
		WRESULT ret = this->ssrvWorker->setAddressPort(value_proposed.toString());
		return WSUCCEEDED(ret);
	 }
	else if ( property_current.getKey() == _TelnetServer_KEY_MaxConnections ) {
		WRESULT ret = this->ssrvWorker->setMaxConnections(value_proposed.toInteger(10));
		return WSUCCEEDED(ret);
	 }
	return BundleGeneric::updatingProperty(do_update, value_proposed, property_current, source);
 }

////////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// CALLBACKS

bool TelnetServerBundle::isAuthorized( TelnetSession* session ) {
	if ( session == NULL ) return false;
	std::string remote_ip = session->getSocket()->getPeerAddressPort();
	Log(LOG_VERBOSE, ":isAuthorized(%ld) : Validating Session from %s..", session->getID(), remote_ip.c_str() );

	//_TelnetServer_KEY_GreenList
	//_TelnetServer_KEY_YellowList
	//_TelnetServer_KEY_BlackList

	return true;
 }

void TelnetServerBundle::session_registration( short reg_unreg, TelnetSession* session ) {
	if ( session == NULL ) return;
	this->ssrvWorker->getSessions().transactionBeginWrite();
	this->ssrvWorker->getSessions().transactionEnd();
 } 

void TelnetServerBundle::session_connected( TelnetSession* session ) {
	if ( session == NULL ) return;
	if ( !isAuthorized(session) ) {
		Log(LOG_INFO, ":session_connected(%ld) : Closing unauthorized Session..", session->getID() );
		session->close(0);
		return;
	 }
	session->setCreatorURI(getURI());
	session->setRestriction( Session::ALLOW_ROOT, Properties.getValueOf(_TelnetServer_KEY_GrantRootAccess).toBoolean(false) );
	session->setRestriction( Session::ALLOW_SU, Properties.getValueOf(_TelnetServer_KEY_GrantSudo).toBoolean(false) );
	session->setMaxLoginRetries( Properties.getValueOf(_TelnetServer_KEY_MaxRetries).toInteger(3) );
	session->setWelcomeMessage( Properties.getValueOf(_TelnetServer_KEY_WelcomeString).toString() );
	session->login();
	Log(LOG_INFO, ":session_connected(%ld) : Registering Session..", session->getID() );
	WoshKernel::getInstance()->sessions().registerSession(session);
 }

void TelnetServerBundle::session_disconnected( TelnetSession* session ) {
	if ( session == NULL ) return;
	Log(LOG_INFO, ":session_disconnected(%ld) : Unregistering Session..", session->getID() );
	WoshKernel::getInstance()->sessions().unRegisterSession(session);
 }

void TelnetServerBundle::session_error( TelnetSession* session ) {
	if ( session == NULL ) return;
	Log(LOG_WARNING, ":session_error(%ld) : ERROR!?", session->getID() );

 }

/////////////////////////////////////////////////////////////////////////////////////////////////// CALLBACKS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* TelnetServerBundle::mmDoDisconnect( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();


	return request->replyResponse(WRET_ERR_PARAM, "TODO");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace services
}; // namespace wosh
